#!/usr/bin/env node

/**
 * 1. 创建一个koa引用
 * 2. 中间件 使用koa-send传输静态文件
 * 3. 中间件 修改第三方模块路径, 替换 process.env.NODE_ENV 为 development 
 *    具体实现：
 *      streamToString 将ctx.body 转换成 string
 *      字符串匹配替换 第三方模块引入（即路径不是. 或 / 开头的 import），替换成形如/@modules/vue
 *      字符串匹配替换 process.dev.NODE_ENV 为 development (当前在浏览器执行，没有process，也没有打包处理，需要替换)
 * 4. 中间件 加载第三方模块
 * 5. 中间件 处理单文件组件
 *    位置：单文件组件读取完成后，即传输静态文件之后。单文件组件可能加载第三方模块，即修改第三方模块路径之前。
 *    具体实现：
 *      streamToString 将ctx.body 转换成 string
 *      使用vue提供的compilerSFC模块parse编译单文件组件
 *      一次请求，不带查询参数，按vite项目修改字符串
 *      二次请求，带查询参数 template，compilerSFC模块compileTemplate编译template
 *      字符串转换成流，返回给浏览器
 * 
 */
const path = require("path");
const Koa = require("koa");
const koaSend = require("koa-send");
const { Readable } = require("stream");
const compilerSFC = require("@vue/compiler-sfc");
const { text } = require("express");

const app = new Koa();

function streamToString(stream) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    stream.on("data", (chunk) => chunks.push(chunk));
    stream.on("end", () => {
      resolve(Buffer.concat(chunks).toString("utf-8"));
    });
    stream.on("error", reject);
  });
}

function stringToStream(string) {
  const stream = new Readable();
  stream.push(string);
  stream.push(null);
  return stream;
}

let count = 1;
// 加载第三方模块
app.use(async (ctx, next) => {
  count++;
  console.log("加载第三方模块", count, ctx.path);
  if (ctx.path.startsWith("/@modules/")) {
    const moduleName = ctx.path.substr(10);
    const pkgPath = path.join(
      process.cwd(),
      "node_modules",
      moduleName,
      "package.json"
    );
    const pkg = require(pkgPath);
    ctx.path = path.join("/node_modules", moduleName, pkg.module);
  }
  await next();
});

// 传输静态文件
app.use(async (ctx, next) => {
  console.log("传输静态文件", count, ctx.path);
  await koaSend(ctx, ctx.path, { root: process.cwd(), index: "index.html" });
  await next(); // 执行下一个中间件
});

// 处理单文件组件
app.use(async (ctx, next) => {
  if (ctx.path.endsWith(".vue")) {
    const contents = await streamToString(ctx.body);
    const { descriptor } = compilerSFC.parse(contents);
    let code;
    if (!ctx.query.type) {
      code = descriptor.script.content;
      code = code.replace(/export\s+default\s+/g, "const __script = ");

      code += `
        import { render as __render } from "${ctx.path}?type=template"
        __script.render = __render
        export default __script
        `;
    } else if (ctx.query.type === "template") {
      const templateRender = compilerSFC.compileTemplate({
        source: descriptor.template.content,
      });
      code = templateRender.code;
    }
    console.log(typeof code);
    ctx.type = "application/javascript";
    ctx.body = stringToStream(code);
  }
  await next();
});

// 修改第三方模块路径
// import vue from 'vue' => import vue from '/@modules/vue'
// 替换 process.env.NODE_ENV 为 development
app.use(async (ctx, next) => {
  console.log("修改第三方模块路径", count, ctx.path);
  if (ctx.type === "application/javascript") {
    const contents = await streamToString(ctx.body);
    //
    ctx.body = contents
      .replace(/(from\s+['"])(?![\.\/])/g, "$1/@modules/")
      .replace(/process\.env\.NODE_ENV/g, '"development"');
  }
  await next();
});

// 加载第三方模块

// 加载第三方模块
app.use(async (ctx, next) => {
  await next();
});

app.listen(3000);
console.log("Server is running at http://localhost:3000");
